/*
* Nettle Source File.
* Copyright (C) 2013, Nettle (http://nttl.ru/).
*
* This file is part of NettleMath.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this diLibution, and is available at
* http://www.gnu.org/licenses/lgpl-2.1.html
*
* This library is diLibuted in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Description:
* Microsoft HLSL intrinsic functions? see more at
* http://msdn.microsoft.com/en-us/library/windows/desktop/ff471376(v=vs.85).aspx
*
* History:
* Created by Sergey Serb, l-proger@yandex.ru
*/

#ifndef NVector4Intrin_h__
#define NVector4Intrin_h__

#include "NVector4.h"

namespace NettleMath{
	//math functions
	//abs ++
	template<typename T>
	inline NVector4<T> abs(const NVector4<T>& v){
		return NVector4<T>(std::abs(v.x), std::abs(v.y), std::abs(v.z), std::abs(v.w));
	}

	template<typename T>
	inline void abs(const NVector4<T>& v, NVector4<T>& out_abs){
		out_abs.x = std::abs(v.x);
		out_abs.y = std::abs(v.y);
		out_abs.z = std::abs(v.z);
		out_abs.w = std::abs(v.w);
	}

	//acos ++
	template<typename T>
	inline NVector4<T> acos(const NVector4<T>& v){
		return NVector4<T>(std::acos(v.x), std::acos(v.y), std::acos(v.z), std::acos(v.w));
	}

	template<typename T>
	inline void acos(const NVector4<T>& v, NVector4<T>& out_acos){
		out_acos.x = std::acos(v.x);
		out_acos.y = std::acos(v.y);
		out_acos.z = std::acos(v.z);
		out_acos.w = std::acos(v.w);
	}

	//all *
	template<typename T>
	inline bool all(const NVector4<T>& v){
		return
			(v.x != static_cast<T>(0)) &&
			(v.y != static_cast<T>(0)) &&
			(v.z != static_cast<T>(0)) &&
			(v.w != static_cast<T>(0));
	}
	//any *
	template<typename T>
	inline bool any(const NVector4<T>& v){
		return
			(v.x != static_cast<T>(0)) ||
			(v.y != static_cast<T>(0)) ||
			(v.z != static_cast<T>(0)) ||
			(v.w != static_cast<T>(0));
	}

	//asin ++
	template<typename T>
	inline NVector4<T> asin(const NVector4<T>& v){
		return NVector4<T>(std::asin(v.x), std::asin(v.y), std::asin(v.z), std::asin(v.w));
	}

	template<typename T>
	inline void asin(const NVector4<T>& v, NVector4<T>& out_asin){
		out_asin.x = std::asin(v.x);
		out_asin.y = std::asin(v.y);
		out_asin.z = std::asin(v.z);
		out_asin.w = std::asin(v.w);
	}

	//atan ++
	template<typename T>
	inline NVector4<T> atan(const NVector4<T>& v){
		return NVector4<T>(std::atan(v.x), std::atan(v.y), std::atan(v.z), std::atan(v.w));
	}

	template<typename T>
	inline void atan(const NVector4<T>& v, NVector4<T>& out_atan){
		out_atan.x = std::atan(v.x);
		out_atan.y = std::atan(v.y);
		out_atan.z = std::atan(v.z);
		out_atan.w = std::atan(v.w);
	}

	//atan2
	template<typename T>
	inline NVector4<T> atan2(const NVector4<T>& vy, const NVector4<T>& vx){
		return NVector4<T>(
			std::atan2(vy.x, vx.x), std::atan2(vy.y, vx.y),
			std::atan2(vy.z, vx.z), std::atan2(vy.w, vx.w));
	}

	template<typename T>
	inline void atan2(const NVector4<T>& v, NVector4<T>& out_atan2){
		out_atan2.x = std::atan2(v.x);
		out_atan2.y = std::atan2(v.y);
		out_atan2.z = std::atan2(v.z);
		out_atan2.w = std::atan2(v.w);
	}

	//ceil ++
	template<typename T>
	inline NVector4<T> ceil(const NVector4<T>& v){
		return NVector4<T>(std::ceil(v.x), std::ceil(v.y), std::ceil(v.z), std::ceil(v.w));
	}

	template<typename T>
	inline void ceil(const NVector4<T>& v, NVector4<T>& out_ceil){
		out_ceil.x = std::ceil(v.x);
		out_ceil.y = std::ceil(v.y);
		out_ceil.z = std::ceil(v.z);
		out_ceil.w = std::ceil(v.w);
	}

	//clamp ++
	template<typename T>
	inline NVector4<T> clamp(const NVector4<T>& v, const NVector4<T>& min, const NVector4<T>& max){
		return NVector4<T>(
			std::min(std::max(v.x, min.x), max.x),
			std::min(std::max(v.y, min.y), max.y),
			std::min(std::max(v.z, min.z), max.z),
			std::min(std::max(v.w, min.w), max.w));
	}

	template<typename T>
	inline void clamp(const NVector4<T>& v, const NVector4<T>& min, const NVector4<T>& max, NVector4<T>& out_clamp){
		out_clamp.x = std::min(std::max(v.x, min.x), max.x);
		out_clamp.y = std::min(std::max(v.y, min.y), max.y);
		out_clamp.z = std::min(std::max(v.z, min.z), max.z);
		out_clamp.w = std::min(std::max(v.w, min.w), max.w);
	}

	//cos++
	template<typename T>
	inline NVector4<T> cos(const NVector4<T>& v){
		return NVector4<T>(std::cos(v.x), std::cos(v.y), std::cos(v.z), std::cos(v.w));
	}

	template<typename T>
	inline void cos(const NVector4<T>& v, NVector4<T>& out_cos){
		out_cos.x = std::cos(v.x);
		out_cos.y = std::cos(v.y);
		out_cos.z = std::cos(v.z);
		out_cos.w = std::cos(v.w);
	}

	//cosh ++
	template<typename T>
	inline NVector4<T> cosh(const NVector4<T>& v){
		return NVector4<T>(std::cosh(v.x), std::cosh(v.y), std::cosh(v.z), std::cosh(v.w));
	}

	template<typename T>
	inline void cosh(const NVector4<T>& v, NVector4<T>& out_cosh){
		out_cosh.x = std::cosh(v.x);
		out_cosh.y = std::cosh(v.y);
		out_cosh.z = std::cosh(v.z);
		out_cosh.w = std::cosh(v.w);
	}

	//degrees ++
	template<typename T>
	inline NVector4<T> degrees(const NVector4<T>& v){
		return NVector4<T>(
			RAD_TO_DEG(v.x),
			RAD_TO_DEG(v.y),
			RAD_TO_DEG(v.z),
			RAD_TO_DEG(v.w));
	}

	template<typename T>
	inline void degrees(const NVector4<T>& v, NVector4<T>& out_degrees){
		out_degrees.x = RAD_TO_DEG(v.x);
		out_degrees.y = RAD_TO_DEG(v.y);
		out_degrees.z = RAD_TO_DEG(v.z);
		out_degrees.w = RAD_TO_DEG(v.w);
	}

	template<typename T>
	inline T distance(const NVector4<T>& v1, const NVector4<T>& v2){
		return (v2 - v1).Length();
	}

	template<typename T>
	inline T dot(const NVector4<T>& v1, const NVector4<T>& v2){
		return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w);
	}
	//exp ++
	template<typename T>
	inline NVector4<T> exp(const NVector4<T>& v){
		return NVector4<T>(std::exp(v.x), std::exp(v.y), std::exp(v.z), std::exp(v.w));
	}

	template<typename T>
	inline void exp(const NVector4<T>& v, NVector4<T>& out_exp){
		out_exp.x = std::exp(v.x);
		out_exp.y = std::exp(v.y);
		out_exp.z = std::exp(v.z);
		out_exp.w = std::exp(v.w);
	}

	//floor ++
	template<typename T>
	inline NVector4<T> floor(const NVector4<T>& v){
		return NVector4<T>(std::floor(v.x), std::floor(v.y), std::floor(v.z), std::floor(v.w));
	}

	template<typename T>
	inline void floor(const NVector4<T>& v, NVector4<T>& out_floor){
		out_floor.x = std::floor(v.x);
		out_floor.y = std::floor(v.y);
		out_floor.z = std::floor(v.z);
		out_floor.w = std::floor(v.w);
	}

	//fmod ++
	template<typename T>
	inline NVector4<T> fmod(const NVector4<T>& vx, const NVector4<T>& vy){
		return NVector4<T>(
			std::fmod(vx.x, vy.x), std::fmod(vx.y, vy.y),
			std::fmod(vx.z, vy.z), std::fmod(vx.w, vy.w));
	}

	template<typename T>
	inline void fmod(const NVector4<T>& v, NVector4<T>& out_fmod){
		out_fmod.x = std::fmod(v.x);
		out_fmod.y = std::fmod(v.y);
		out_fmod.z = std::fmod(v.z);
		out_fmod.w = std::fmod(v.w);
	}

	//frac ++
	template<typename T>
	inline NVector4<T> frac(const NVector4<T>& v){
		return NVector4<T>(frac(v.x), frac(v.y), frac(v.z), frac(v.w));
	}

	template<typename T>
	inline void frac(const NVector4<T>& v, NVector4<T>& out_frac){
		out_frac.x = frac(v.x);
		out_frac.y = frac(v.y);
		out_frac.z = frac(v.z);
		out_frac.w = frac(v.w);
	}

	template<typename T>
	inline T length(const NVector4<T>& v){
		return std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w);
	}

	//lerp ++
	template<typename T>
	inline NVector4<T> lerp(const NVector4<T>& x, const NVector4<T>& y, const NVector4<T>& s){
		return x + (y - x) * s;
	}

	template<typename T>
	inline void lerp(const NVector4<T>& x, const NVector4<T>& y, const NVector4<T>& s, NVector4<T>& out_lerp){
		out_lerp.x = x.x + (y.x - x.x) * s.x;
		out_lerp.y = x.y + (y.y - x.y) * s.y;
		out_lerp.z = x.z + (y.z - x.z) * s.z;
		out_lerp.w = x.w + (y.w - x.w) * s.w;
	}
	//log ++
	template<typename T>
	inline NVector4<T> log(const NVector4<T>& v){
		return NVector4<T>(std::log(v.x), std::log(v.y), std::log(v.z), std::log(v.w));
	}

	template<typename T>
	inline void log(const NVector4<T>& v, NVector4<T>& out_log){
		out_log.x = std::log(v.x);
		out_log.y = std::log(v.y);
		out_log.z = std::log(v.z);
		out_log.w = std::log(v.w);
	}

	//log10 ++
	template<typename T>
	inline NVector4<T> log10(const NVector4<T>& v){
		return NVector4<T>(std::log10(v.x), std::log10(v.y), std::log10(v.z), std::log10(v.w));
	}

	template<typename T>
	inline void log10(const NVector4<T>& v, NVector4<T>& out_log10){
		out_log10.x = std::log10(v.x);
		out_log10.y = std::log10(v.y);
		out_log10.z = std::log10(v.z);
		out_log10.w = std::log10(v.w);
	}

	//mad ++
	template<typename T>
	inline NVector4<T> mad(const NVector4<T>& m, const NVector4<T>& a, const NVector4<T>& b){
		return NVector4<T>(m.x * a.x + b.x, m.y * a.y + b.y, m.z * a.z + b.z, m.w * a.w + b.w);
	}

	template<typename T>
	inline void mad(const NVector4<T>& m, const NVector4<T>& a, const NVector4<T>& b, NVector4<T>& out_mad){
		out_mad.x = m.x * a.x + b.x;
		out_mad.y = m.y * a.y + b.y;
		out_mad.z = m.z * a.z + b.z;
		out_mad.w = m.w * a.w + b.w;
	}

	//max ++
	template<typename T>
	inline NVector4<T> max(const NVector4<T>& vx, const NVector4<T>& vy){
		return NVector4<T>(std::max(vx.x, vy.x), std::max(vx.y, vy.y), std::max(vx.z, vy.z), std::max(vx.w, vy.w));
	}

	template<typename T>
	inline void max(const NVector4<T>& vx, const NVector4<T>& vy, NVector4<T>& out_max){
		out_max.x = std::max(vx.x, vy.x);
		out_max.y = std::max(vx.y, vy.y);
		out_max.z = std::max(vx.z, vy.z);
		out_max.w = std::max(vx.w, vy.w);
	}



	template<typename T>
	inline void min(const NVector4<T>& vx, const NVector4<T>& vy, NVector4<T>& out_min){
		out_min.x = std::min(vx.x, vy.x);
		out_min.y = std::min(vx.y, vy.y);
		out_min.z = std::min(vx.z, vy.z);
		out_min.w = std::min(vx.w, vy.w);
	}

	template<typename T, typename T2>
	inline NVector4<T> modf(const NVector4<T>& x, NVector4<T2>& out_ip){
		NMATH_STATIC_ASSERT(std::is_signed<T2>::value, "modf: integer part must be unsigned");
		NMATH_STATIC_ASSERT(!(std::is_integral<T>::value), "modf: useless operation on integer vector");
		out_ip.x = static_cast<T2>(x.x < 0 ? std::ceil(x.x) : std::floor(x.x));
		out_ip.y = static_cast<T2>(x.y < 0 ? std::ceil(x.y) : std::floor(x.y));
		out_ip.z = static_cast<T2>(x.z < 0 ? std::ceil(x.z) : std::floor(x.z));
		out_ip.w = static_cast<T2>(x.w < 0 ? std::ceil(x.w) : std::floor(x.w));
		return NVector4<T>(x.x - out_ip.x, x.y - out_ip.y, x.z - out_ip.z, x.w - out_ip.w);
	}

	//mul ++++
	template<typename T>
	inline NVector4<T> mul(const T val, const NVector4<T>& v){
		return NVector4<T>(val * v.x, val * v.y, val * v.z, val * v.w);
	}

	template<typename T>
	inline void mul(const T val, const NVector4<T>& v, NVector4<T>& out_mul){
		return NVector4<T>(val * v.x, val * v.y, val * v.z, val * v.w);
	}

	template<typename T>
	inline NVector4<T> mul(const NVector4<T>& v, const T val){
		return mul(val, v);
	}

	template<typename T>
	inline void mul(const NVector4<T>& v, const T val, NVector4<T>& out_mul){
		mul(val, v, out_mul);
	}

	//normalize ++
	template<typename T>
	inline NVector4<T> normalize(const NVector4<T>& v){
		NVector4<T> result;
		T vectorLength = length(v);
		assert((vectorLength != 0) && "normalize: vectorLength is zero!");
		if (vectorLength != 0){
			result.x = v.x / vectorLength;
			result.y = v.y / vectorLength;
			result.z = v.z / vectorLength;
			result.w = v.w / vectorLength;
		}
		return result;
	}

	template<typename T>
	inline void normalize(const NVector4<T>& v, NVector4<T>& out_norm){
		T vectorLength = length(v);
		assert((vectorLength != 0) && "normalize: vectorLength is zero!");
		if (vectorLength != 0){
			out_norm.x = v.x / vectorLength;
			out_norm.y = v.y / vectorLength;
			out_norm.z = v.z / vectorLength;
			out_norm.w = v.w / vectorLength;
		}
	}

	//pow ++
	template<typename T>
	inline NVector4<T> pow(const NVector4<T>& val, const NVector4<T>& power){
		return NVector4<T>(
			std::pow(val.x, power.x), std::pow(val.y, power.y),
			std::pow(val.z, power.z), std::pow(val.w, power.w));
	}

	template<typename T>
	inline void pow(const NVector4<T>& val, const NVector4<T>& power, NVector4<T>& out_pow){
		out_pow.x = std::pow(val.x, power.x);
		out_pow.y = std::pow(val.y, power.y);
		out_pow.z = std::pow(val.z, power.z);
		out_pow.w = std::pow(val.w, power.w);
	}

	//radians ++
	template<typename T>
	inline NVector4<T> radians(const NVector4<T>& v){
		return NVector4<T>(
			DEG_TO_RAD(v.x),
			DEG_TO_RAD(v.y),
			DEG_TO_RAD(v.z),
			DEG_TO_RAD(v.w));
	}

	template<typename T>
	inline void radians(const NVector4<T>& v, NVector4<T>& out_deg){
		out_deg.x = DEG_TO_RAD(v.x);
		out_deg.y = DEG_TO_RAD(v.y);
		out_deg.z = DEG_TO_RAD(v.z);
		out_deg.w = DEG_TO_RAD(v.w);
	}

	//rcp ++
	template<typename T>
	inline NVector4<T> rcp(const NVector4<T>& x){
		NMATH_STATIC_ASSERT(!(std::is_integral<T>::value), "rcp: useless operation on integer vector");
		return NVector4<T>(
			static_cast<T>(1) / x.x,
			static_cast<T>(1) / x.y,
			static_cast<T>(1) / x.z,
			static_cast<T>(1) / x.w);
	}

	template<typename T>
	inline void rcp(const NVector4<T>& x, NVector4<T>& out_rcp){
		out_rcp.x = static_cast<T>(1) / x.x;
		out_rcp.y = static_cast<T>(1) / x.y;
		out_rcp.z = static_cast<T>(1) / x.z;
		out_rcp.w = static_cast<T>(1) / x.w;
	}

	//reflect ++
	template<typename T>
	inline NVector4<T> reflect(const NVector4<T>& i, const NVector4<T>& n){
		T dot_val2 = static_cast<T>(2) * dot(i, n);
		return NVector4<T>(
			i.x - dot_val2 * n.x,
			i.y - dot_val2 * n.y,
			i.z - dot_val2 * n.z,
			i.w - dot_val2 * n.w);
	}

	template<typename T>
	inline void reflect(const NVector4<T>& i, const NVector4<T>& n, NVector4<T>& out_reflect){
		T dot_val2 = static_cast<T>(2) * dot(i, n);
		out_reflect.x = i.x - dot_val2 * n.x;
		out_reflect.y = i.y - dot_val2 * n.y;
		out_reflect.z = i.z - dot_val2 * n.z;
		out_reflect.w = i.w - dot_val2 * n.w;
	}

	//refract ++
	template<typename T>
	inline NVector4<T> refract(const NVector4<T>&i, const NVector4<T>&n, float eta){
		T N_dot_I = dot(n, i);
		T k = static_cast<T>(1) - eta * eta * (static_cast<T>(1) - N_dot_I * N_dot_I);
		if (k < static_cast<T>(0))
			return NVector4<T>(static_cast<T>(0));
		else
			return i * eta - (eta * N_dot_I + sqrt(k)) * n;
	}

	template<typename T>
	inline void refract(const NVector4<T>&i, const NVector4<T>&n, float eta, NVector4<T>& out_refract){
		T N_dot_I = dot(n, i);
		T k = static_cast<T>(1) - eta * eta * (static_cast<T>(1) - N_dot_I * N_dot_I);
		if (k < static_cast<T>(0))
			out_refract.x = out_refract.y = out_refract.z = out_refract.w = static_cast<T>(0);
		else{
			out_refract.x = i.x * eta - (eta * N_dot_I + sqrt(k)) * n.x;
			out_refract.y = i.y * eta - (eta * N_dot_I + sqrt(k)) * n.y;
			out_refract.z = i.z * eta - (eta * N_dot_I + sqrt(k)) * n.z;
			out_refract.w = i.w * eta - (eta * N_dot_I + sqrt(k)) * n.w;
		}
	}

	//reversebits ++
	template<typename T>
	inline NVector4<T> reversebits(const NVector4<T>& value){
		NMATH_STATIC_ASSERT(std::is_integral<T>::value, "reversebits: forbidden operation on non-integer vector");
		size_t bit_count = sizeof(T) * NMATH_BYTE_SIZE;
		NVector4<T> result;
		for(size_t i = 0; i < bit_count; i++){
			result.x = (result.x << 0x1) | ((value.x >> i) & 0x1);
			result.y = (result.y << 0x1) | ((value.y >> i) & 0x1);
			result.z = (result.z << 0x1) | ((value.z >> i) & 0x1);
			result.w = (result.w << 0x1) | ((value.w >> i) & 0x1);
		}
		return result;
	}

	template<typename T>
	inline void reversebits(const NVector4<T>& value, NVector4<T>& out_reversebits){
		NMATH_STATIC_ASSERT(std::is_integral<T>::value, "reversebits: forbidden operation on non-integer vector");
		size_t bit_count = sizeof(T) * NMATH_BYTE_SIZE;
		for(size_t i = 0; i < bit_count; i++){
			out_reversebits.x = (out_reversebits.x << 0x1) | ((value.x >> i) & 0x1);
			out_reversebits.y = (out_reversebits.y << 0x1) | ((value.y >> i) & 0x1);
			out_reversebits.z = (out_reversebits.z << 0x1) | ((value.z >> i) & 0x1);
			out_reversebits.w = (out_reversebits.w << 0x1) | ((value.w >> i) & 0x1);
		}
	}

	//round ++
	template<typename T>
	inline NVector4<T> round(const NVector4<T>& x){
		NMATH_STATIC_ASSERT(!(std::is_integral<T>::value), "round: useless operation on integer vector");
		T half_offset = static_cast<T>(0.5);
		return NVector4<T>(
			std::floor(x.x + half_offset),
			std::floor(x.y + half_offset),
			std::floor(x.z + half_offset),
			std::floor(x.w + half_offset));
	}

	template<typename T>
	inline void round(const NVector4<T>& x, NVector4<T>& out_round){
		NMATH_STATIC_ASSERT(!(std::is_integral<T>::value), "round: useless operation on integer vector");
		T half_offset = static_cast<T>(0.5);
		out_round.x = std::floor(x.x + half_offset);
		out_round.y = std::floor(x.y + half_offset);
		out_round.z = std::floor(x.z + half_offset);
		out_round.w = std::floor(x.w + half_offset);
	}

	//rsqrt ++
	template<typename T>
	inline NVector4<T> rsqrt(const NVector4<T>& x){
		NMATH_STATIC_ASSERT(!(std::is_integral<T>::value), "round: useless operation on integer vector");
		return NVector4<T>(
			static_cast<T>(1) / sqrt(x.x),
			static_cast<T>(1) / sqrt(x.y),
			static_cast<T>(1) / sqrt(x.z),
			static_cast<T>(1) / sqrt(x.w));
	}

	template<typename T>
	inline void rsqrt(const NVector4<T>& x, NVector4<T>& out_rsqrt){
		NMATH_STATIC_ASSERT(!(std::is_integral<T>::value), "round: useless operation on integer vector");
		out_rsqrt.x = static_cast<T>(1) / sqrt(x.x);
		out_rsqrt.y = static_cast<T>(1) / sqrt(x.y);
		out_rsqrt.z = static_cast<T>(1) / sqrt(x.z);
		out_rsqrt.w = static_cast<T>(1) / sqrt(x.w);
	}

	//saturate ++
	template<typename T>
	inline NVector4<T> saturate(const NVector4<T>& x){
		return NVector4<T>(
			std::max(static_cast<T>(0), std::min(static_cast<T>(1), x.x)),
			std::max(static_cast<T>(0), std::min(static_cast<T>(1), x.y)),
			std::max(static_cast<T>(0), std::min(static_cast<T>(1), x.z)),
			std::max(static_cast<T>(0), std::min(static_cast<T>(1), x.w)));
	}

	template<typename T>
	inline void saturate(const NVector4<T>& x, NVector4<T>& out_saturate){
		out_saturate.x = std::max(static_cast<T>(0), std::min(static_cast<T>(1), x.x));
		out_saturate.y = std::max(static_cast<T>(0), std::min(static_cast<T>(1), x.y));
		out_saturate.z = std::max(static_cast<T>(0), std::min(static_cast<T>(1), x.z));
		out_saturate.w = std::max(static_cast<T>(0), std::min(static_cast<T>(1), x.w));
	}

	//sign ++
	template<typename T>
	inline NVector4<T> sign(const NVector4<T>& x){
		return NVector4<T>(
			x.x < 0 ? -1 : (x.x == 0 ? 0 : 1),
			x.y < 0 ? -1 : (x.y == 0 ? 0 : 1),
			x.z < 0 ? -1 : (x.z == 0 ? 0 : 1),
			x.w < 0 ? -1 : (x.w == 0 ? 0 : 1));
	}

	template<typename T>
	inline void sign(const NVector4<T>& x, NVector4<T>& out_sign){
		out_sign.x = x.x < 0 ? -1 : (x.x == 0 ? 0 : 1);
		out_sign.y = x.y < 0 ? -1 : (x.y == 0 ? 0 : 1);
		out_sign.z = x.z < 0 ? -1 : (x.z == 0 ? 0 : 1);
		out_sign.w = x.w < 0 ? -1 : (x.w == 0 ? 0 : 1);
	}

	//sin ++
	template<typename T>
	inline NVector4<T> sin(const NVector4<T>& x){
		return NVector4<T>(
			std::sin(x.x),
			std::sin(x.y),
			std::sin(x.z),
			std::sin(x.w));
	}

	template<typename T>
	inline NVector4<T> sin(const NVector4<T>& x, NVector4<T>& out_sin){
		out_sin.x = std::sin(x.x);
		out_sin.y = std::sin(x.y);
		out_sin.z = std::sin(x.z);
		out_sin.w = std::sin(x.w);
	}

	//sincos +
	template<typename T>
	inline void sincos(const NVector4<T>& x, NVector4<T>& s, NVector4<T>& c){
		s.x = std::sin(x.x);
		s.y = std::sin(x.y);
		s.z = std::sin(x.z);
		s.w = std::sin(x.w);
		c.x = std::cos(x.x);
		c.y = std::cos(x.y);
		c.z = std::cos(x.z);
		c.w = std::cos(x.w);
	}

	//sinh ++
	template<typename T>
	inline NVector4<T> sinh(const NVector4<T>& x){
		return NVector4<T>(
			std::sinh(x.x),
			std::sinh(x.y),
			std::sinh(x.z),
			std::sinh(x.w));
	}

	template<typename T>
	inline NVector4<T> sinh(const NVector4<T>& x, NVector4<T>& out_sinh){
		out_sinh.x = std::sinh(x.x);
		out_sinh.y = std::sinh(x.y);
		out_sinh.z = std::sinh(x.z);
		out_sinh.w = std::sinh(x.w);
	}

	/*//smoothstep ++
	template<typename T>
	inline NVector4<T> smoothstep(const NVector4<T>& edge0, const NVector4<T>& edge1, const NVector4<T>& x){
		NVector4<T> t = clamp ((x – edge0) / (edge1 – edge0), static_cast<T>(0), static_cast<T>(1));
		return NVector4<T>(
			t.x * t.x * (3 - 2 * t.x),
			t.y * t.y * (3 - 2 * t.y),
			t.z * t.z * (3 - 2 * t.z),
			t.w * t.w * (3 - 2 * t.w));
	}

	template<typename T>
	inline NVector4<T> smoothstep(const NVector4<T>& edge0, const NVector4<T>& edge1, const NVector4<T>& x, NVector4<T>& out_smoothstep){
		NVector4<T> t = clamp ((x – edge0) / (edge1 – edge0), static_cast<T>(0), static_cast<T>(1));
		out_smoothstep.x = t.x * t.x * (3 - 2 * t.x);
		out_smoothstep.y = t.y * t.y * (3 - 2 * t.y);
		out_smoothstep.z = t.z * t.z * (3 - 2 * t.z);
		out_smoothstep.w = t.w * t.w * (3 - 2 * t.w);
	}*/

	//sqrt ++
	template<typename T>
	inline NVector4<T> sqrt(const NVector4<T>& x){
		NMATH_STATIC_ASSERT(!(std::is_integral<T>::value), "round: useless operation on integer vector");
		return NVector4<T>(sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w));
	}

	template<typename T>
	inline void sqrt(const NVector4<T>& x, NVector4<T>& out_sqrt){
		NMATH_STATIC_ASSERT(!(std::is_integral<T>::value), "round: useless operation on integer vector");
		out_sqrt.x = sqrt(x.x);
		out_sqrt.y = sqrt(x.y);
		out_sqrt.z = sqrt(x.z);
		out_sqrt.w = sqrt(x.w);
	}

	//step ++
	template<typename T>
	inline NVector4<T> step(const NVector4<T>& y, const NVector4<T>& x){
		return NVector4<T>(
			(x.x >= y.x) ? 1 : 0,
			(x.y >= y.y) ? 1 : 0,
			(x.z >= y.z) ? 1 : 0,
			(x.w >= y.w) ? 1 : 0);
	}

	template<typename T, typename T_RET>
	inline void step(const NVector4<T>& y, const NVector4<T>& x, NVector4<T>& out_step){
		out_step.x = (x.x >= y.x) ? 1 : 0;
		out_step.y = (x.y >= y.y) ? 1 : 0;
		out_step.z = (x.z >= y.z) ? 1 : 0;
		out_step.w = (x.w >= y.w) ? 1 : 0;
	}

	//tan ++
	template<typename T>
	inline NVector4<T> tan(const NVector4<T>& x){
		return NVector4<T>(std::tan(x.x), std::tan(x.y), std::tan(x.z), std::tan(x.w));
	}

	template<typename T>
	inline void tan(const NVector4<T>& x, NVector4<T>& out_tan){
		out_tan.x = std::tan(x.x);
		out_tan.y = std::tan(x.y);
		out_tan.z = std::tan(x.z);
		out_tan.w = std::tan(x.w);
	}

	//tanh ++
	template<typename T>
	inline NVector4<T> tanh(const NVector4<T>& x){
		return NVector4<T>(std::tanh(x.x), std::tanh(x.y), std::tanh(x.z), std::tanh(x.w));
	}

	template<typename T>
	inline void tanh(const NVector4<T>& x, NVector4<T>& out_tan){
		out_tan.x = std::tanh(x.x);
		out_tan.y = std::tanh(x.y);
		out_tan.z = std::tanh(x.z);
		out_tan.w = std::tanh(x.w);
	}

	//trunc
	template<typename T>
	inline NVector4<T> trunc(const NVector4<T>& x){
        return NVector4<T>(
            static_cast<T>(static_cast<int64>(x.x)),
            static_cast<T>(static_cast<int64>(x.y)),
            static_cast<T>(static_cast<int64>(x.z)),
            static_cast<T>(static_cast<int64>(x.w))
            );
	}
}
#endif // NVector4Intrin_h__

